#include "vsf.h"
#include "component/port/linux/vsflinux.h"

#include <stdlib.h>
#include <stdarg.h>

#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <poll.h>
#include <semaphore.h>

#include <sys/time.h>
#include <sys/select.h>

#ifndef VSFLINUX_CFG_MAXARGS
#	define VSFLINUX_CFG_MAXARGS		31
#endif
#ifndef VSFLINUX_CFG_MAX_FD
#	define VSFLINUX_CFG_MAX_FD		256
#endif
#ifndef VSFLINUX_CFG_FD_CACHE
#	define VSFLINUX_CFG_FD_CACHE	1024
#endif
#ifndef VSFLINUX_CFG_STACKSIZE
#	define VSFLINUX_CFG_STACKSIZE	0
#endif

struct vsflinux_t
{
	struct
	{
		uint32_t mskarr[(VSFLINUX_CFG_MAX_FD + 31) / 32];
		struct vsf_dynarr_t arr;
	} fd;

	int curtid;
	struct vsflist_t threadlist;
} static vsflinux;

static vsf_err_t vsflinux_create_fhs(void)
{
	return VSFERR_NONE;
}

extern int vsflinux_libc_init(void);
vsf_err_t vsflinux_init(void)
{
	vsf_err_t err = vsflinux_create_fhs();
	if (err) return err;

	vsflinux.curtid = 1;
	vsflinux.threadlist.next = NULL;

	memset(vsflinux.fd.mskarr, 0, sizeof(vsflinux.fd.mskarr));
	vsflinux.fd.arr.item_size = sizeof(struct vsflinux_fd_t *);
	vsflinux.fd.arr.item_num_bitlen = 4;
	vsflinux.fd.arr.table_size_bitlen = 4;
	err = vsf_dynarr_init(&vsflinux.fd.arr);
	if (err) return err;

	return vsflinux_libc_init();
}

struct vsfsm_thread_t *vsflinux_thread_get(int tid)
{
	struct vsfsm_thread_t *thread = NULL;
	uint8_t origlevel = vsfsm_sched_lock();

	vsflist_foreach(lthread, vsflinux.threadlist.next, struct vsflinux_thread_t, list)
	{
		if (lthread->tid == tid)
		{
			thread = (struct vsfsm_thread_t *)lthread - 1;
			break;
		}
	}
	vsfsm_sched_unlock(origlevel);
	return thread;
}

struct vsflinux_main_priv_t
{
	struct vsflinux_thread_t thread;

	int argc;
	char const *argv[VSFLINUX_CFG_MAXARGS + 1];
	int (*entry)(int argc, char* argv[]);
};

static void vsflinux_main_on_run(struct vsfsm_thread_t *thread)
{
	struct vsflinux_main_priv_t *priv = (struct vsflinux_main_priv_t *)thread->priv;
	priv->thread.retval = (uint32_t)priv->entry(priv->argc, (char **)priv->argv);
}

void vsflinux_thread_on_terminate(struct vsfsm_thread_t *thread)
{
	struct vsflinux_thread_t *lthread = (struct vsflinux_thread_t *)thread->priv;
	uint8_t origlevel;

	if (lthread->pending != NULL)
	{
		((struct vsflinux_thread_t *)lthread->pending->priv)->retval = lthread->retval;
		vsfsm_thread_sendevt(lthread->pending, VSFSM_EVT_USER);
	}

	origlevel = vsfsm_sched_lock();
	vsflist_remove(&vsflinux.threadlist.next, &lthread->list);
	vsfsm_sched_unlock(origlevel);
	free(thread);
}

static const struct vsfsm_thread_op_t vsflinux_main_op =
{
	.on_run = vsflinux_main_on_run,
	.on_terminate = vsflinux_thread_on_terminate,
};

struct vsfsm_thread_t *vsflinux_thread_new(int priv_size, int stack_size,
		const struct vsfsm_thread_op_t *op)
{
	struct vsfsm_thread_t *thread = NULL;
	struct vsflinux_thread_t *priv;
	uint8_t origlevel;

	if (!stack_size) stack_size = VSFLINUX_CFG_THREAD_STACKSIZE;
	thread = (struct vsfsm_thread_t *)malloc(priv_size + sizeof(struct vsfsm_thread_t) + stack_size);
	if (thread != NULL)
	{
		priv = (struct vsflinux_thread_t *)&thread[1];

		origlevel = vsfsm_sched_lock();
		priv->tid = vsflinux.curtid++;
		vsfsm_sched_unlock(origlevel);

		memset(thread, 0, sizeof(struct vsfsm_thread_t) + priv_size);
		thread->priv = priv;
		thread->op = *op;
		thread->stack_size = stack_size;
		thread->stack = (uint8_t *)priv + priv_size;
		memset(thread->stack, 0xCC, stack_size);

		priv->main = vsfsm_thread_get_cur();
		priv->main = !priv->main ? thread : ((struct vsflinux_thread_t *)priv->main->priv)->main;

		origlevel = vsfsm_sched_lock();
		priv->list.next = vsflinux.threadlist.next;
		vsflinux.threadlist.next = &priv->list;
		vsfsm_sched_unlock(origlevel);
	}

	return thread;
}

static struct vsfsm_thread_t *vsflinux_exec_new(void)
{
	struct vsfsm_thread_t *thread;

	thread = vsfsm_thread_get_cur();
	if (!thread)
		thread = vsflinux_thread_new(sizeof(struct vsflinux_main_priv_t), VSFLINUX_CFG_EXE_STACKSIZE,
					&vsflinux_main_op);
	else
		thread = ((struct vsflinux_thread_t *)thread->priv)->main;
	return thread;
}

static int vsflinux_exec_run(struct vsfsm_thread_t *thread)
{
	if (thread == vsfsm_thread_get_cur())
	{
		// attach to current thread
		vsfsm_post_evt(&thread->sm, VSFSM_EVT_INIT);
	}
	else
	{
		// start new thread
		vsfsm_thread_start(thread);
	}
	return 0;
}

int vsflinux_execv(int (*entry)(int argc, char *argv[]), char *const argv[])
{
	struct vsfsm_thread_t *thread = vsflinux_exec_new();
	struct vsflinux_main_priv_t *priv;

	if (!thread) return -1;
	priv = (struct vsflinux_main_priv_t *)thread->priv;

	priv->argc = 0;
	while ((*argv != NULL) && (priv->argc <= VSFLINUX_CFG_MAXARGS))
		priv->argv[priv->argc++] = *argv++;
	priv->argv[priv->argc] = (char *)NULL;
	priv->entry = entry;

	return vsflinux_exec_run(thread);
}

int vsflinux_execl(int (*entry)(int argc, char *argv[]), ...)
{
	struct vsfsm_thread_t *thread = vsflinux_exec_new();
	struct vsflinux_main_priv_t *priv;
	const char *args;
	va_list ap;

	if (!thread) return -1;
	priv = (struct vsflinux_main_priv_t *)thread->priv;

	priv->argc = 0;
	va_start(ap, entry);
	args = va_arg(ap, const char *);
	while ((args != NULL) && (priv->argc <= VSFLINUX_CFG_MAXARGS))
	{
		priv->argv[priv->argc++] = args;
		args = va_arg(ap, const char *);
	}
	va_end(ap);
	priv->argv[priv->argc] = (char *)NULL;
	priv->entry = entry;

	return vsflinux_exec_run(thread);
}

// sem
int sem_init(sem_t *sem, int pshared, unsigned int value)
{
	vsfsm_sem_init(&sem->vsfsem, value, VSFSM_EVT_USER);
	return 0;
}

int sem_destroy(sem_t *sem)
{
	return 0;
}

int sem_wait(sem_t *sem)
{
	struct vsfsm_thread_t *thread = vsfsm_thread_get_cur();
	if (vsfsm_sem_pend(&sem->vsfsem, &thread->sm))
		vsfsm_thread_wfe(VSFSM_EVT_USER);
	return 0;
}

int sem_trywait(sem_t *sem)
{
	int ret = 0;
	uint8_t origlevel = vsfsm_sched_lock();
	if (sem->vsfsem.cur_value)
		sem->vsfsem.cur_value--;
	else
	{
		errno = EAGAIN;
		ret = -errno;
	}
	vsfsm_sched_unlock(origlevel);
	return ret;
}

int sem_post(sem_t *sem)
{
	vsfsm_sem_post(&sem->vsfsem);
	return 0;
}

int sem_getvalue(sem_t *sem, int *sval)
{
	if (sval)
		*sval = sem->vsfsem.cur_value;
	return 0;
}

// fd
int vsflinux_fd_del(struct vsflinux_fd_t *sfd)
{
	if (sfd->cache_dynalloced)
		free(sfd->cache.fifo.buffer.buffer);
	mskarr_clr(vsflinux.fd.mskarr, sfd->fd);
	free(sfd);
	return 0;
}

int vsflinux_fd_new(struct vsflinux_fd_t **psfd, int priv_size,
		const struct vsflinux_fdop_t *op)
{
	struct vsflinux_fd_t *new_sfd, **new_psfd;

	new_sfd = malloc(sizeof(*new_sfd) + priv_size);
	if (!new_sfd)
	{
	oom:
		errno = ENOMEM;
		return -1;
	}
	memset(new_sfd, 0, sizeof(*new_sfd) + priv_size);
	new_sfd->op = op;
	new_sfd->rxpend = new_sfd->txpend = -1;

	new_sfd->fd = mskarr_ffz(vsflinux.fd.mskarr, VSFLINUX_CFG_MAX_FD);
	if ((new_sfd->fd < 0) || ((new_sfd->fd >= vsf_dynarr_get_size(&vsflinux.fd.arr)) &&
		 	(vsf_dynarr_set_size(&vsflinux.fd.arr, new_sfd->fd + 1) != VSFERR_NONE)))
	{
		free(new_sfd);
		goto oom;
	}
	mskarr_set(vsflinux.fd.mskarr, new_sfd->fd);

	new_psfd = (struct vsflinux_fd_t **)vsf_dynarr_get(&vsflinux.fd.arr, new_sfd->fd);
	*new_psfd = new_sfd;
	if (psfd != NULL)
		*psfd = new_sfd;
	return new_sfd->fd;
}

struct vsflinux_fd_t *vsflinux_fd_get(int fd)
{
	if (mskarr_get(vsflinux.fd.mskarr, fd))
		return *(struct vsflinux_fd_t **)vsf_dynarr_get(&vsflinux.fd.arr, fd);
	return NULL;
}

int vsflinux_fd_free_cache(struct vsflinux_fd_t *sfd)
{
	free(sfd->cache.fifo.buffer.buffer);
	sfd->cache.fifo.buffer.size = 0;
	sfd->cache_dynalloced = false;
	return 0;
}

int vsflinux_fd_alloc_cache(struct vsflinux_fd_t *sfd, int size)
{
	if (!size) size = VSFLINUX_CFG_FD_CACHE;
	sfd->cache.fifo.buffer.buffer = malloc(size);
	if (!sfd->cache.fifo.buffer.buffer)
	{
		errno = ENOMEM;
		return -1;
	}

	sfd->cache.fifo.buffer.size = size;
	sfd->cache_dynalloced = true;
	return 0;
}

int vsflinux_fd_tx_pend(struct vsflinux_fd_t *sfd)
{
	if (sfd->txevt)
		sfd->txevt = false;
	else
	{
		sfd->txpend = ((struct vsflinux_thread_t *)(vsfsm_thread_get_cur())->priv)->tid;
		vsfsm_thread_wfe(VSFSM_EVT_USER);
	}
	return 0;
}

int vsflinux_fd_rx_pend(struct vsflinux_fd_t *sfd)
{
	if (sfd->rxevt)
		sfd->rxevt = false;
	else
	{
		sfd->rxpend = ((struct vsflinux_thread_t *)(vsfsm_thread_get_cur())->priv)->tid;
		vsfsm_thread_wfe(VSFSM_EVT_USER);
	}
	return 0;
}

int vsflinux_fd_tx_sendevt(struct vsflinux_fd_t *sfd)
{
	if (sfd->txpend >= 0)
	{
		struct vsfsm_thread_t *rthread = vsflinux_thread_get(sfd->txpend);
		sfd->txpend = -1;
		vsfsm_thread_sendevt(rthread, VSFSM_EVT_USER);
	}
	else
		sfd->txevt = true;
	return 0;
}

int vsflinux_fd_rx_sendevt(struct vsflinux_fd_t *sfd)
{
	if (sfd->rxpend >= 0)
	{
		struct vsfsm_thread_t *rthread = vsflinux_thread_get(sfd->rxpend);
		sfd->rxpend = -1;
		vsfsm_thread_sendevt(rthread, VSFSM_EVT_USER);
	}
	else
		sfd->rxevt = true;
	return 0;
}

int vsflinux_fd_fcntl(struct vsflinux_fd_t *sfd, int cmd, long arg)
{
	int ret;

	switch (cmd)
	{
	case F_GETFL:
		return sfd->flags;
	case F_SETFL:
		{
			int diff = arg ^ sfd->flags;
			if (diff & O_NONBLOCK)
			{
				ret = (sfd->flags & O_NONBLOCK) ?
					vsflinux_fd_free_cache(sfd) : vsflinux_fd_alloc_cache(sfd, 0);
				if (ret < 0) return ret;
			}
			sfd->flags = arg;
		}
		break;
	default:
		return -1;
		break;
	}
	return 0;
}

// vsflinux_vsfile
static struct vsfile_t *vsflinux_vsfile_get(const char *pathname)
{
	struct vsfile_t *file = NULL;
	struct vsfsm_pt_t pt =
	{
		.sm = &(vsfsm_thread_get_cur())->sm,
		.state = 0,
	};
	vsfsm_evt_t evt = VSFSM_EVT_NONE;
	vsf_err_t err;

	do {
		err = vsfile_getfile(&pt, evt, NULL, (char *)pathname, &file);
		if (err <= 0) break;
		else evt = vsfsm_thread_wait();
	} while (1);

	return file;
}

static void vsflinux_vsfile_close_do(struct vsfile_t *file)
{
	struct vsfsm_pt_t pt =
	{
		.sm = &(vsfsm_thread_get_cur())->sm,
		.state = 0,
	};
	vsfsm_evt_t evt = VSFSM_EVT_NONE;
	vsf_err_t err;

	do {
		err = vsfile_close(&pt, evt, file);
		if (err <= 0) break;
		else evt = vsfsm_thread_wait();
	} while (1);
}

static int vsflinux_vsfile_unlink(const char *pathname)
{
	struct vsfsm_pt_t pt =
	{
		.sm = &(vsfsm_thread_get_cur())->sm,
		.state = 0,
	};
	vsfsm_evt_t evt = VSFSM_EVT_NONE;
	char *name = NULL, *path = NULL, *tmppath;
	struct vsfile_t *dir;
	vsf_err_t err;

	dir = vsflinux_vsfile_get(pathname);
	if (!dir)
	{
		errno = ENOENT;
		return -1;
	}
	if (dir->attr & VSFILE_ATTR_USR)
		name = dir->name;
	vsflinux_vsfile_close_do(dir);
	dir = NULL;

	path = strdup(pathname);
	if (!path)
	{
		errno = ENOMEM;
		return -1;
	}

	tmppath = &path[strlen(path) - 1];
	while ((tmppath > path) && !vsfile_is_div(*tmppath)) tmppath--;
	if (vsfile_is_div(*tmppath))
		*tmppath++ = '\0';
	dir = vsflinux_vsfile_get(path);

	do {
		err = vsfile_removefile(&pt, evt, dir, tmppath);
		if (!err) break;
		else if (err < 0)
		{
			err = -1;
			goto fail;
		}
		else evt = vsfsm_thread_wait();
	} while (1);

	if (name != NULL)
		free(name);
fail:
	if (dir != NULL)
		vsflinux_vsfile_close_do(dir);
	return err;
}

static int vsflinux_vsfile_creat(const char *pathname, enum vsfile_attr_t attr)
{
	struct vsfsm_pt_t pt =
	{
		.sm = &(vsfsm_thread_get_cur())->sm,
	};
	vsfsm_evt_t evt = VSFSM_EVT_NONE;
	char *path = strdup(pathname), *tmppath, *tmpname;
	struct vsfile_t *dir = NULL, *ftmp;
	vsf_err_t err;
	int entry = 0;

	if (!path)
	{
	oom:
		errno = ENOMEM;
	fail:
		err = -1;
		goto exit;
	}

	while (1)
	{
		dir = vsflinux_vsfile_get(path);
		if (dir != NULL) break;

		entry++;
		tmppath = &path[strlen(path) - 1];
		while ((tmppath > path) && !vsfile_is_div(*tmppath)) tmppath--;
		if (!vsfile_is_div(*tmppath)) break;
		*tmppath++ = '\0';
	}

	while (entry-- > 0)
	{
		tmpname = strdup(tmppath);
		if (!tmpname) goto oom;

		pt.state = 0;
		do {
			err = vsfile_addfile(&pt, evt, dir, tmpname, !entry ? attr : VSFILE_ATTR_DIRECTORY);
			if (!err) break;
			else if (err < 0) goto fail;
			else if (evt > 0) evt = vsfsm_thread_wait();
		} while (1);

		pt.state = 0;
		do {
			err = vsfile_getfile(&pt, evt, dir, tmppath, &ftmp);
			if (!err) break;
			else if (err < 0) goto fail;
			else if (evt > 0) evt = vsfsm_thread_wait();
		} while (1);

		ftmp->attr |= VSFILE_ATTR_USR;
		if (dir != NULL)
			vsflinux_vsfile_close_do(dir);
		dir = ftmp;

		tmppath = tmppath + strlen(tmppath) + 1;
	}

exit:
	if (dir != NULL)
		vsflinux_vsfile_close_do(dir);
	if (path != NULL)
		free(path);
	return err;
}

static int vsflinux_vsfile_fcntl(struct vsflinux_fd_t *sfd, int cmd, long arg)
{
	return 0;
}

static ssize_t vsflinux_vsfile_read(struct vsflinux_fd_t *sfd, void *buf, size_t count)
{
	struct vsflinux_vsfile_priv_t *priv = (struct vsflinux_vsfile_priv_t *)sfd->priv;
	struct vsfsm_pt_t pt =
	{
		.sm = &(vsfsm_thread_get_cur())->sm,
		.state = 0,
	};
	vsfsm_evt_t evt = VSFSM_EVT_NONE;
	vsf_err_t err;
	ssize_t readsize = 0;
	uint32_t rsize;

	do {
		err = vsfile_read(&pt, evt, priv->file, priv->pos, count, (uint8_t *)buf, &rsize);
		if (!err)
		{
			count -= rsize;
			readsize += rsize;
			priv->pos += rsize;
			buf = (uint8_t *)buf + rsize;
		}
		else if (err < 0) return -1;
		else evt = vsfsm_thread_wait();
	} while (count > 0);
	return readsize;
}

static ssize_t vsflinux_vsfile_write(struct vsflinux_fd_t *sfd, const void *buf, size_t count)
{
	struct vsflinux_vsfile_priv_t *priv = (struct vsflinux_vsfile_priv_t *)sfd->priv;
	struct vsfsm_pt_t pt =
	{
		.sm = &(vsfsm_thread_get_cur())->sm,
		.state = 0,
	};
	vsfsm_evt_t evt = VSFSM_EVT_NONE;
	vsf_err_t err;
	ssize_t writesize = 0;
	uint32_t wsize;

	do {
		err = vsfile_write(&pt, evt, priv->file, priv->pos, count, (uint8_t *)buf, &wsize);
		if (!err)
		{
			count -= wsize;
			writesize += wsize;
			priv->pos += wsize;
			buf = (uint8_t *)buf + wsize;
		}
		else if (err < 0) return -1;
		else evt = vsfsm_thread_wait();
	} while (count > 0);
	return writesize;
}

static int vsflinux_vsfile_close(struct vsflinux_fd_t *sfd)
{
	struct vsflinux_vsfile_priv_t *priv = (struct vsflinux_vsfile_priv_t *)sfd->priv;
	vsflinux_vsfile_close_do(priv->file);
	return 0;
}

struct vsflinux_fdop_t vsflinux_vsfile_fdop =
{
	.fcntl = vsflinux_vsfile_fcntl,
	.read = vsflinux_vsfile_read,
	.write = vsflinux_vsfile_write,
	.close = vsflinux_vsfile_close,
};

// signal
#if 0
#include <signal.h>
int sigemptyset(sigset_t *set)
{
	
}

int sigfillset(sigset_t *set)
{
}

int sigaddset(sigset_t *set, int signo)
{
}

int sigdelset(sigset_t *set, int signo)
{
}

int sigismember(sigset_t *set, int signo)
{
}

int sigpromask(int how, const sigset_t *set, sigset_t *oldset)
{
}

int sigpending(sigset_t *set)
{
}

int sigsuspend(const sigset_t *sigmask)
{
}

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
{
}

// unistd & fcntl
pid_t fork(void)
{
}

uid_t getuid(void)
{
}

uid_t geteuid(void)
{
}

gid_t getgid(void)
{
}

gid_t getegid(void)
{
}

int setuid(uid_t uid)
{
}

int setgid(gid_t gid)
{
}

int getgroups(int gidsetsize, gid_t grouplist[])
{
}

pid_t getpgrp(void)
{
}

pid_t setsid(void)
{
}

int setpgid(pid_t pid, pid_t pgid)
{
}

int uname(struct utsname *name)
{
}

char *getenv(const char *name)
{
}

char *getcwd(char *buf, size_t size)
{
}

mode_t umask(mode_t cmask)
{
}

int kill(pid_t pid, int sig)
{
}

pid_t wait(int *stat_loc)
{
}

pid_t waitpid(pid_t pid, int *stat_loc, int options)
{
}

void exit(int status)
{
}

unsigned int alarm(unsigned int seconds)
{
}

int pause(void)
{
}

pid_t getppid(void)
{
}
#endif

gid_t getgid(void)
{
	return 0;
}

gid_t getegid(void)
{
	return 0;
}

uid_t getuid(void)
{
	return 0;
}

uid_t geteuid(void)
{
	return 0;
}

pid_t getpid(void)
{
	return 0;
}

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
		struct timeval *timeout)
{
	return 0;
}

int poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
	int tid = ((struct vsflinux_thread_t *)(vsfsm_thread_get_cur())->priv)->tid;
	struct vsflinux_fd_t *sfd;
	int ret = 0;
	nfds_t i;

	do {
		for (i = 0; i < nfds; i++)
		{
			sfd = vsflinux_fd_get(fds[i].fd);
			if (sfd->rxevt || sfd->txevt)
			{
				if ((fds[i].events & POLLIN) && sfd->rxevt)
					fds[i].revents |= POLLIN;
				if ((fds[i].events & POLLOUT) && sfd->txevt)
					fds[i].revents |= POLLOUT;
				if (fds[i].revents)
					ret++;
			}
		}
		if (ret) return ret;

		for (i = 0; i < nfds; i++)
		{
			sfd = vsflinux_fd_get(fds[i].fd);
			if (fds[i].events & POLLIN)
				sfd->rxpend = tid;
			if (fds[i].events & POLLOUT)
				sfd->txpend = tid;
		}
		vsfsm_thread_wfe(VSFSM_EVT_USER);
		for (i = 0; i < nfds; i++)
		{
			sfd = vsflinux_fd_get(fds[i].fd);
			if (fds[i].events & POLLIN)
			{
				if (sfd->rxpend < 0)
					sfd->rxevt = true;
				else
					sfd->rxpend = -1;
			}
			if (fds[i].events & POLLOUT)
			{
				if (sfd->txpend < 0)
					sfd->txevt = true;
				else
					sfd->txpend = -1;
			}
		}
	} while (1);
}

int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout,
			const sigset_t *sigmask)
{
	return poll(fds, nfds, 0);
}

int stat(const char *pathname, struct stat *buf)
{
	return 0;
}

int access(const char *pathname, int mode)
{
	int fd = open(pathname, mode);
	if (fd < 0) return -1;
	else
	{
		close(fd);
		return 0;
	}
}

int unlink(const char *pathname)
{
	return vsflinux_vsfile_unlink(pathname);
}

int creat(const char *pathname, mode_t mode)
{
	if (vsflinux_vsfile_creat(pathname, VSFILE_ATTR_ARCHIVE) < 0)
		return -1;
	return open(pathname, 0);
}

ssize_t write(int fd, const void *buf, size_t count)
{
	struct vsflinux_fd_t *sfd = vsflinux_fd_get(fd);
	if (!sfd || (sfd->flags & O_RDONLY)) return -1;
	return sfd->op->write(sfd, buf, count);
}

ssize_t read(int fd, void *buf, size_t count)
{
	struct vsflinux_fd_t *sfd = vsflinux_fd_get(fd);
	if (!sfd || (sfd->flags & O_WRONLY)) return -1;
	return sfd->op->read(sfd, buf, count);
}

int fcntl(int fd, int cmd, ...)
{
	struct vsflinux_fd_t *sfd = vsflinux_fd_get(fd);
	va_list ap;
	long arg;

	if (!sfd) return -1;

	va_start(ap, cmd);
	arg = va_arg(ap, long);
	va_end(ap);
	return sfd->op->fcntl(sfd, cmd, arg);
}

int open(const char *pathname, int flags, ...)
{
	struct vsfile_t *file = vsflinux_vsfile_get(pathname);
	struct vsflinux_vsfile_priv_t *priv;
	struct vsflinux_fd_t *sfd;
	int fd = -1;

	if (!file && (flags & O_CREAT))
	{
		va_list ap;
		mode_t mode;

		va_start(ap, flags);
		mode = va_arg(ap, mode_t);
		va_end(ap);

		return creat(pathname, mode);
	}
	if (!file)
		return -1;

	if (fd < 0)
		fd = vsflinux_fd_new(&sfd, sizeof(*priv), &vsflinux_vsfile_fdop);
	if (fd < 0)
		vsflinux_vsfile_close_do(file);
	else
	{
		sfd->flags = flags;
		priv = (struct vsflinux_vsfile_priv_t *)sfd->priv;
		priv->file = file;

		if ((flags & O_TRUNC) && (file->attr & VSFILE_ATTR_ARCHIVE))
		{
			// TODO: 
		}
	}

	return fd;
}

int close(int fd)
{
	struct vsflinux_fd_t *sfd = vsflinux_fd_get(fd);
	int err;

	if (!sfd) return -1;
	err = sfd->op->close(sfd);
	vsflinux_fd_del(sfd);
	return err;
}

// dirent
#include <dirent.h>
DIR *opendir(const char *name)
{
	
}

extern int closedir(DIR *dirp)
{
	
}

struct dirent *readdir(DIR *dirp)
{
}

int getrlimit(int resource, struct rlimit *rlp)
{
}

int setrlimit(int resource, const struct rlimit *rlp)
{
}

int usleep(unsigned int useconds)
{
	struct vsfsm_thread_t *thread = vsfsm_thread_get_cur();
	int ms = (useconds + 999) / 1000;
	vsftimer_create(&thread->sm, ms, 1, VSFSM_EVT_USER);
	vsfsm_thread_wfe(VSFSM_EVT_USER);
	return 0;
}

unsigned int sleep(unsigned int seconds)
{
	return usleep(seconds * 1000 * 1000);
}

int nanosleep(const struct timespec *requested_time, struct timespec *remaining)
{
	if (requested_time->tv_sec)
		sleep(requested_time->tv_sec);
	if (requested_time->tv_nsec)
		usleep(requested_time->tv_sec / 1000);
	if (remaining != NULL)
	{
		remaining->tv_sec = 0;
		remaining->tv_nsec = 0;
	}
	return 0;
}

int clock_gettime(clockid_t clk_id, struct timespec *tp)
{
	switch (clk_id)
	{
	case CLOCK_MONOTONIC:
		{
			uint32_t ms = vsfhal_tickclk_get_ms();
			tp->tv_sec = ms / 1000;
			tp->tv_nsec = (ms % 1000) * 1000 * 1000;
		}
		return 0;
	default:
		return -1;
	}
}

int gettimeofday(struct timeval *tv, struct timezone *tz)
{
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC, &ts);
	tv->tv_sec = ts.tv_sec;
	tv->tv_usec = ts.tv_nsec / 1000;
	return 0;
}

int getitimer(int which, struct itimerval *curr_value)
{
}

int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value)
{
}
